/*
 * Copyright (c) 2016, The Linux Foundation. All rights reserved.
 * Not a Contribution.
 *
 * Copyright (C) 2015 NXP Semiconductors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Internal Download Management routines
 * Download Component
 */

#include <phDnldNfc_Internal.h>
#include <phDnldNfc_Utils.h>
#include <phTmlNfc.h>
#include <phNxpLog.h>
#include <phNxpNciHal_utils.h>

#define PHDNLDNFC_MIN_PLD_LEN   (0x04U)                      /* Minimum length of payload including 1 byte CmdId */

#define PHDNLDNFC_FRAME_HDR_OFFSET  (0x00)                    /* Offset of Length byte within the frame */
#define PHDNLDNFC_FRAMEID_OFFSET (PHDNLDNFC_FRAME_HDR_LEN)        /* Offset of FrameId within the frame */
#define PHDNLDNFC_FRAMESTATUS_OFFSET PHDNLDNFC_FRAMEID_OFFSET  /* Offset of status byte within the frame */
#define PHDNLDNFC_PLD_OFFSET    (PHDNLDNFC_MIN_PLD_LEN - 1)    /* Offset within frame where payload starts*/

#define PHDNLDNFC_FRAME_RDDATA_OFFSET ((PHDNLDNFC_FRAME_HDR_LEN) + \
                                      (PHDNLDNFC_MIN_PLD_LEN)) /* recvd frame offset where data starts */

#define PHDNLDNFC_FRAME_SIGNATURE_SIZE   (0xC0U)        /* Size of first secure write frame Signature */
#define PHDNLDNFC_FIRST_FRAME_PLD_SIZE   (0xE4U)        /* Size of first secure write frame payload */

#define PHDNLDNFC_FIRST_FRAGFRAME_RESP   (0x2DU)        /* Status response for first fragmented write frame */
#define PHDNLDNFC_NEXT_FRAGFRAME_RESP    (0x2EU)        /* Status response for subsequent fragmented write frame */

#define PHDNLDNFC_SET_HDR_FRAGBIT(n)      ((n) | (1<<10))    /* Header chunk bit set macro */
#define PHDNLDNFC_CLR_HDR_FRAGBIT(n)      ((n) & ~(1U<<10))   /* Header chunk bit clear macro */
#define PHDNLDNFC_CHK_HDR_FRAGBIT(n)      ((n) & 0x04)       /* macro to check if frag bit is set in Hdr */

#define PHDNLDNFC_RSP_TIMEOUT   (2500)                /* Timeout value to wait for response from NFCC */
#define PHDNLDNFC_RETRY_FRAME_WRITE   (50)            /* Timeout value to wait before resending the last frame */

#define PHDNLDNFC_USERDATA_EEPROM_LENSIZE    (0x02U)    /* size of EEPROM user data length */
#define PHDNLDNFC_USERDATA_EEPROM_OFFSIZE    (0x02U)    /* size of EEPROM offset */

#ifdef  NXP_PN547C1_DOWNLOAD
/* EEPROM offset and length value for PN547C1 */
#define PHDNLDNFC_USERDATA_EEPROM_OFFSET  (0x003CU)    /* 16 bits offset indicating user data area start location */
#define PHDNLDNFC_USERDATA_EEPROM_LEN     (0x0DC0U)    /* 16 bits length of user data area */
#else

#if(NFC_NXP_CHIP_TYPE == PN547C2)
/* EEPROM offset and length value for PN547C2 */
#define PHDNLDNFC_USERDATA_EEPROM_OFFSET  (0x023CU)    /* 16 bits offset indicating user data area start location */
#define PHDNLDNFC_USERDATA_EEPROM_LEN     (0x0C80U)    /* 16 bits length of user data area */
#else
/* EEPROM offset and length value for PN548AD */
#define PHDNLDNFC_USERDATA_EEPROM_OFFSET  (0x02BCU)    /* 16 bits offset indicating user data area start location */
#define PHDNLDNFC_USERDATA_EEPROM_LEN     (0x0C00U)    /* 16 bits length of user data area */
#endif

#endif
#define PH_LIBNFC_VEN_RESET_ON_DOWNLOAD_TIMEOUT (1)

/* Function prototype declarations */
static void phDnldNfc_ProcessSeqState(void *pContext, phTmlNfc_TransactInfo_t *pInfo);
static void phDnldNfc_ProcessRWSeqState(void *pContext, phTmlNfc_TransactInfo_t *pInfo);
static NFCSTATUS phDnldNfc_ProcessFrame(void *pContext, phTmlNfc_TransactInfo_t *pInfo);
static NFCSTATUS phDnldNfc_ProcessRecvInfo(void *pContext, phTmlNfc_TransactInfo_t *pInfo);
static NFCSTATUS phDnldNfc_BuildFramePkt(pphDnldNfc_DlContext_t pDlContext);
static NFCSTATUS phDnldNfc_CreateFramePld(pphDnldNfc_DlContext_t pDlContext);
static NFCSTATUS phDnldNfc_SetupResendTimer(pphDnldNfc_DlContext_t pDlContext);
static NFCSTATUS phDnldNfc_UpdateRsp(pphDnldNfc_DlContext_t   pDlContext, phTmlNfc_TransactInfo_t  *pInfo, uint16_t wPldLen);
static void phDnldNfc_RspTimeOutCb(uint32_t TimerId, void *pContext);
static void phDnldNfc_ResendTimeOutCb(uint32_t TimerId, void *pContext);

/*
*************************** Function Definitions ***************************
*/

/*******************************************************************************
**
** Function         phDnldNfc_CmdHandler
**
** Description      Download Command Handler Mechanism
**                  - holds the sub states for each command processing
**                  - coordinates with TML download thread to complete a download command request
**                  - calls the user callback on completion of a cmd
**
** Parameters       pContext  - pointer to the download context structure
**                  TrigEvent - event requested by user
**
** Returns          NFC status:
**                  NFCSTATUS_PENDING           - download request sent to NFCC successfully,response pending
**                  NFCSTATUS_BUSY              - handler is busy processing a download request
**                  NFCSTATUS_INVALID_PARAMETER - one or more of the supplied parameters could not be interpreted properly
**                  Other errors                -
**
*******************************************************************************/
NFCSTATUS phDnldNfc_CmdHandler(void *pContext, phDnldNfc_Event_t TrigEvent)
{
    NFCSTATUS  status = NFCSTATUS_SUCCESS;
    pphDnldNfc_DlContext_t  pDlCtxt = (pphDnldNfc_DlContext_t)pContext;

    if(NULL == pDlCtxt)
    {
      NXPLOG_FWDNLD_E("Invalid Input Parameter!!");
      status = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_INVALID_PARAMETER);
    }
    else
    {
        switch(TrigEvent)
        {
            case phDnldNfc_EventReset:
            case phDnldNfc_EventGetVer:
            case phDnldNfc_EventIntegChk:
            case phDnldNfc_EventGetSesnSt:
            case phDnldNfc_EventRaw:
            {
                if(phDnldNfc_EventInvalid == (pDlCtxt->tCurrEvent))
                {
                    NXPLOG_FWDNLD_D("Processing Normal Sequence..");
                    pDlCtxt->tCurrEvent = TrigEvent;
                    pDlCtxt->tDnldInProgress = phDnldNfc_TransitionBusy;

                    phDnldNfc_ProcessSeqState(pDlCtxt,NULL);

                    status = pDlCtxt->wCmdSendStatus;
                }
                else
                {
                    NXPLOG_FWDNLD_E("Prev Norml Sequence not completed/restored!!");
                    status = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_FAILED);
                }
                break;
            }
            case phDnldNfc_EventWrite:
            case phDnldNfc_EventRead:
            case phDnldNfc_EventLog:
            case phDnldNfc_EventForce:
            {
                if(phDnldNfc_EventInvalid == (pDlCtxt->tCurrEvent))
                {
                    NXPLOG_FWDNLD_D("Processing R/W Sequence..");
                    pDlCtxt->tCurrEvent = TrigEvent;
                    pDlCtxt->tDnldInProgress = phDnldNfc_TransitionBusy;

                    phDnldNfc_ProcessRWSeqState(pDlCtxt,NULL);

                    status = pDlCtxt->wCmdSendStatus;
                }
                else
                {
                    NXPLOG_FWDNLD_E("Prev R/W Sequence not completed/restored!!");
                    status = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_FAILED);
                }
                break;
            }
            default:
            {
                /* Unknown Event */
                NXPLOG_FWDNLD_E("Unknown Event Parameter!!");
                status = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_INVALID_PARAMETER);
                break;
            }
        }
    }

    return status;
}

/*******************************************************************************
**
** Function         phDnldNfc_ProcessSeqState
**
** Description      Processes all cmd/resp sequences except read & write
**
** Parameters       pContext - pointer to the download context structure
**                  pInfo    - pointer to the Transaction buffer updated by TML Thread
**
** Returns          None
**
*******************************************************************************/
static void phDnldNfc_ProcessSeqState(void *pContext, phTmlNfc_TransactInfo_t *pInfo)
{
    NFCSTATUS wStatus = NFCSTATUS_SUCCESS;
    NFCSTATUS wIntStatus;
    uint32_t TimerId;
    pphDnldNfc_DlContext_t pDlCtxt = (pphDnldNfc_DlContext_t)pContext;

    if(NULL == pDlCtxt)
    {
        NXPLOG_FWDNLD_E("Invalid Input Parameter!!");
        wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_INVALID_PARAMETER);
    }
    else
    {
        switch(pDlCtxt->tCurrState)
        {
            case phDnldNfc_StateInit:
            {
                NXPLOG_FWDNLD_D("Initializing Sequence..");

                if(0 == (pDlCtxt->TimerInfo.dwRspTimerId))
                {
                    TimerId = phOsalNfc_Timer_Create();

                    if (0 == TimerId)
                    {
                        NXPLOG_FWDNLD_W("Response Timer Create failed!!");
                        wStatus = NFCSTATUS_INSUFFICIENT_RESOURCES;
                        pDlCtxt->wCmdSendStatus = wStatus;
                        break;
                    }
                    else
                    {
                        NXPLOG_FWDNLD_D("Response Timer Created Successfully");
                        (pDlCtxt->TimerInfo.dwRspTimerId) = TimerId;
                        (pDlCtxt->TimerInfo.TimerStatus) = 0;
                        (pDlCtxt->TimerInfo.wTimerExpStatus) = 0;
                    }
                }
                pDlCtxt->tCurrState = phDnldNfc_StateSend;
            }
            case phDnldNfc_StateSend:
            {
                wStatus = phDnldNfc_BuildFramePkt(pDlCtxt);

                if(NFCSTATUS_SUCCESS == wStatus)
                {
                    pDlCtxt->tCurrState = phDnldNfc_StateRecv;

                    wStatus = phTmlNfc_Write( (pDlCtxt->tCmdRspFrameInfo.aFrameBuff),
                        (uint16_t)(pDlCtxt->tCmdRspFrameInfo.dwSendlength),
                                    (pphTmlNfc_TransactCompletionCb_t)&phDnldNfc_ProcessSeqState,
                                    pDlCtxt);
                }
                pDlCtxt->wCmdSendStatus = wStatus;
                break;
            }
            case phDnldNfc_StateRecv:
            {
                wStatus = phDnldNfc_ProcessRecvInfo(pContext,pInfo);

                if(NFCSTATUS_SUCCESS == wStatus)
                {
                    wStatus = phOsalNfc_Timer_Start((pDlCtxt->TimerInfo.dwRspTimerId),
                                                PHDNLDNFC_RSP_TIMEOUT,
                                                &phDnldNfc_RspTimeOutCb,
                                                pDlCtxt);

                    if (NFCSTATUS_SUCCESS == wStatus)
                    {
                        NXPLOG_FWDNLD_D("Response timer started");
                        pDlCtxt->TimerInfo.TimerStatus = 1;
                        pDlCtxt->tCurrState = phDnldNfc_StateTimer;
                    }
                    else
                    {
                         NXPLOG_FWDNLD_W("Response timer not started");
                        pDlCtxt->tCurrState = phDnldNfc_StateResponse;
                    }
                    /* Call TML_Read function and register the call back function */
                    wStatus = phTmlNfc_Read(
                        pDlCtxt->tCmdRspFrameInfo.aFrameBuff,
                        (uint16_t )PHDNLDNFC_CMDRESP_MAX_BUFF_SIZE,
                        (pphTmlNfc_TransactCompletionCb_t)&phDnldNfc_ProcessSeqState,
                        (void *)pDlCtxt);

                    /* set read status to pDlCtxt->wCmdSendStatus to enable callback */
                    pDlCtxt->wCmdSendStatus = wStatus;
                    break;
                }
                else
                {
                    /* Setting TimerExpStatus below to avoid frame processing in response state */
                    (pDlCtxt->TimerInfo.wTimerExpStatus) = NFCSTATUS_RF_TIMEOUT;
                    pDlCtxt->tCurrState = phDnldNfc_StateResponse;
                }
            }
            case phDnldNfc_StateTimer:
            {
                if (1 == (pDlCtxt->TimerInfo.TimerStatus)) /*Is Timer Running*/
                {
                    /*Stop Timer*/
                    (void)phOsalNfc_Timer_Stop(pDlCtxt->TimerInfo.dwRspTimerId);
                    (pDlCtxt->TimerInfo.TimerStatus) = 0; /*timer stopped*/
                }
                pDlCtxt->tCurrState = phDnldNfc_StateResponse;
            }
            case phDnldNfc_StateResponse:
            {
                if(NFCSTATUS_RF_TIMEOUT != (pDlCtxt->TimerInfo.wTimerExpStatus))
                {
                    /* Process response */
                    wStatus = phDnldNfc_ProcessFrame(pContext,pInfo);
                }
                else
                {
                    if(phDnldNfc_EventReset != pDlCtxt->tCurrEvent)
                    {
                        wStatus = (pDlCtxt->TimerInfo.wTimerExpStatus);
                    }
                    else
                    {
                        wStatus = NFCSTATUS_SUCCESS;
                    }
                    (pDlCtxt->TimerInfo.wTimerExpStatus) = 0;
                }

                /* Abort TML read operation which is always kept open */
                wIntStatus  = phTmlNfc_ReadAbort();

                if(NFCSTATUS_SUCCESS != wIntStatus)
                {
                    /* TODO:-Action to take in this case:-Tml read abort failed!? */
                    NXPLOG_FWDNLD_W("Tml Read Abort failed!!");
                }

                pDlCtxt->tCurrEvent = phDnldNfc_EventInvalid;
                pDlCtxt->tDnldInProgress = phDnldNfc_TransitionIdle;
                pDlCtxt->tCurrState = phDnldNfc_StateInit;

                /* Delete the timer & reset timer primitives in context */
                (void)phOsalNfc_Timer_Delete(pDlCtxt->TimerInfo.dwRspTimerId);
                (pDlCtxt->TimerInfo.dwRspTimerId) = 0;
                (pDlCtxt->TimerInfo.TimerStatus) = 0;
                (pDlCtxt->TimerInfo.wTimerExpStatus) = 0;

                if((NULL != (pDlCtxt->UserCb)) && (NULL != (pDlCtxt->UserCtxt)))
                {
                    pDlCtxt->UserCb((pDlCtxt->UserCtxt),wStatus,&(pDlCtxt->tRspBuffInfo));
                }
                break;
            }
            default:
            {
                pDlCtxt->tCurrEvent = phDnldNfc_EventInvalid;
                pDlCtxt->tDnldInProgress = phDnldNfc_TransitionIdle;
                break;
            }
        }
    }

    return;
}

/*******************************************************************************
**
** Function         phDnldNfc_ProcessRWSeqState
**
** Description      Processes read/write cmd/rsp sequence
**
** Parameters       pContext - pointer to the download context structure
**                  pInfo    - pointer to the Transaction buffer updated by TML Thread
**
** Returns          None
**
*******************************************************************************/
static void phDnldNfc_ProcessRWSeqState(void *pContext, phTmlNfc_TransactInfo_t *pInfo)
{
    NFCSTATUS wStatus = NFCSTATUS_SUCCESS;
    NFCSTATUS wIntStatus = wStatus;
    uint32_t TimerId;
    pphDnldNfc_DlContext_t pDlCtxt = (pphDnldNfc_DlContext_t)pContext;

    if(NULL == pDlCtxt)
    {
        NXPLOG_FWDNLD_E("Invalid Input Parameter!!");
        wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_INVALID_PARAMETER);
    }
    else
    {
        switch(pDlCtxt->tCurrState)
        {
            case phDnldNfc_StateInit:
            {
                if(0 == (pDlCtxt->TimerInfo.dwRspTimerId))
                {
                    TimerId = phOsalNfc_Timer_Create();

                    if (0 == TimerId)
                    {
                        NXPLOG_FWDNLD_E("Response Timer Create failed!!");
                        wStatus = NFCSTATUS_INSUFFICIENT_RESOURCES;
                    }
                    else
                    {
                        NXPLOG_FWDNLD_D("Response Timer Created Successfully");
                        (pDlCtxt->TimerInfo.dwRspTimerId) = TimerId;
                        (pDlCtxt->TimerInfo.TimerStatus) = 0;
                        (pDlCtxt->TimerInfo.wTimerExpStatus) = 0;
                    }
                }
                pDlCtxt->tCurrState = phDnldNfc_StateSend;
            }
            case phDnldNfc_StateSend:
            {
                if(FALSE == pDlCtxt->bResendLastFrame)
                {
                    wStatus = phDnldNfc_BuildFramePkt(pDlCtxt);
                }
                else
                {
                    pDlCtxt->bResendLastFrame = FALSE;
                }

                if(NFCSTATUS_SUCCESS == wStatus)
                {
                    pDlCtxt->tCurrState = phDnldNfc_StateRecv;

                    wStatus = phTmlNfc_Write((pDlCtxt->tCmdRspFrameInfo.aFrameBuff),
                        (uint16_t)(pDlCtxt->tCmdRspFrameInfo.dwSendlength),
                                    (pphTmlNfc_TransactCompletionCb_t)&phDnldNfc_ProcessRWSeqState,
                                    pDlCtxt);
                }
                pDlCtxt->wCmdSendStatus = wStatus;
                break;
            }
            case phDnldNfc_StateRecv:
            {
                wStatus = phDnldNfc_ProcessRecvInfo(pContext,pInfo);

                if(NFCSTATUS_SUCCESS == wStatus)
                {
                    /* processing For Pipelined write before calling timer below */
                    wStatus = phOsalNfc_Timer_Start((pDlCtxt->TimerInfo.dwRspTimerId),
                                                PHDNLDNFC_RSP_TIMEOUT,
                                                &phDnldNfc_RspTimeOutCb,
                                                pDlCtxt);

                    if (NFCSTATUS_SUCCESS == wStatus)
                    {
                        NXPLOG_FWDNLD_D("Response timer started");
                        pDlCtxt->TimerInfo.TimerStatus = 1;
                        pDlCtxt->tCurrState = phDnldNfc_StateTimer;
                    }
                    else
                    {
                         NXPLOG_FWDNLD_W("Response timer not started");
                        pDlCtxt->tCurrState = phDnldNfc_StateResponse;
                        /* Todo:- diagnostic in this case */
                    }
                    /* Call TML_Read function and register the call back function */
                    wStatus = phTmlNfc_Read(
                        pDlCtxt->tCmdRspFrameInfo.aFrameBuff,
                        (uint16_t )PHDNLDNFC_CMDRESP_MAX_BUFF_SIZE,
                        (pphTmlNfc_TransactCompletionCb_t)&phDnldNfc_ProcessRWSeqState,
                        (void *)pDlCtxt);

                    /* set read status to pDlCtxt->wCmdSendStatus to enable callback */
                    pDlCtxt->wCmdSendStatus = wStatus;
                    break;
                }
                else
                {
                    /* Setting TimerExpStatus below to avoid frame processing in reponse state */
                    (pDlCtxt->TimerInfo.wTimerExpStatus) = NFCSTATUS_RF_TIMEOUT;
                    pDlCtxt->tCurrState = phDnldNfc_StateResponse;
                }
            }
            case phDnldNfc_StateTimer:
            {
                if (1 == (pDlCtxt->TimerInfo.TimerStatus)) /*Is Timer Running*/
                {
                    /* Stop Timer */
                    (void)phOsalNfc_Timer_Stop(pDlCtxt->TimerInfo.dwRspTimerId);
                    (pDlCtxt->TimerInfo.TimerStatus) = 0; /*timer stopped*/
                }
                pDlCtxt->tCurrState = phDnldNfc_StateResponse;
            }
            case phDnldNfc_StateResponse:
            {
                if(NFCSTATUS_RF_TIMEOUT != (pDlCtxt->TimerInfo.wTimerExpStatus))
                {
                    /* Process response */
                    wStatus = phDnldNfc_ProcessFrame(pContext,pInfo);

                    if(NFCSTATUS_BUSY == wStatus)
                    {
                        /* store the status for use in subsequent processing */
                        wIntStatus = wStatus;

                        /* setup the resend wait timer */
                        wStatus = phDnldNfc_SetupResendTimer(pDlCtxt);

                        if(NFCSTATUS_SUCCESS == wStatus)
                        {
                            /* restore the last mem_bsy status to avoid re-building frame below */
                            wStatus = wIntStatus;
                        }
                    }
                }
                else
                {
                    wStatus = (pDlCtxt->TimerInfo.wTimerExpStatus);
                    (pDlCtxt->TimerInfo.wTimerExpStatus) = 0;
                }

                if((0 != (pDlCtxt->tRWInfo.wRemBytes)) && (NFCSTATUS_SUCCESS == wStatus))
                {
                    /* Abort TML read operation which is always kept open */
                    wIntStatus  = phTmlNfc_ReadAbort();

                    if(NFCSTATUS_SUCCESS != wIntStatus)
                    {
                         NXPLOG_FWDNLD_W("Tml read abort failed!");
                    }

                    wStatus = phDnldNfc_BuildFramePkt(pDlCtxt);

                    if(NFCSTATUS_SUCCESS == wStatus)
                    {
                        pDlCtxt->tCurrState = phDnldNfc_StateRecv;
                        wStatus = phTmlNfc_Write((pDlCtxt->tCmdRspFrameInfo.aFrameBuff),
                            (uint16_t)(pDlCtxt->tCmdRspFrameInfo.dwSendlength),
                                        (pphTmlNfc_TransactCompletionCb_t)&phDnldNfc_ProcessRWSeqState,
                                        pDlCtxt);

                        /* TODO:- Verify here if TML_Write returned NFC_PENDING status & take appropriate
                              action otherwise ?? */
                    }
                }
                else if(NFCSTATUS_BUSY == wStatus)
                {
                    /* No processing to be done,since resend wait timer should have already been started */
                }
                else
                {
                    (pDlCtxt->tRWInfo.bFramesSegmented) = FALSE;
                    /* Abort TML read operation which is always kept open */
                    wIntStatus  = phTmlNfc_ReadAbort();

                    if(NFCSTATUS_SUCCESS != wIntStatus)
                    {
                         NXPLOG_FWDNLD_W("Tml read abort failed!");
                    }

                    pDlCtxt->tCurrEvent = phDnldNfc_EventInvalid;
                    pDlCtxt->tDnldInProgress = phDnldNfc_TransitionIdle;
                    pDlCtxt->tCurrState = phDnldNfc_StateInit;
                    pDlCtxt->bResendLastFrame = FALSE;

                    /* Delete the timer & reset timer primitives in context */
                    (void)phOsalNfc_Timer_Delete(pDlCtxt->TimerInfo.dwRspTimerId);
                    (pDlCtxt->TimerInfo.dwRspTimerId) = 0;
                    (pDlCtxt->TimerInfo.TimerStatus) = 0;
                    (pDlCtxt->TimerInfo.wTimerExpStatus) = 0;

                    if((NULL != (pDlCtxt->UserCb)) && (NULL != (pDlCtxt->UserCtxt)))
                    {
                        pDlCtxt->UserCb((pDlCtxt->UserCtxt),wStatus,&(pDlCtxt->tRspBuffInfo));
                    }
                }
                break;
            }
            default:
            {
                pDlCtxt->tCurrEvent = phDnldNfc_EventInvalid;
                pDlCtxt->tDnldInProgress = phDnldNfc_TransitionIdle;
                break;
            }
        }
    }

    return;
}

/*******************************************************************************
**
** Function         phDnldNfc_BuildFramePkt
**
** Description      Forms the frame packet
**
** Parameters       pDlContext - pointer to the download context structure
**
** Returns          NFC status
**
*******************************************************************************/
static NFCSTATUS phDnldNfc_BuildFramePkt(pphDnldNfc_DlContext_t pDlContext)
{
    NFCSTATUS wStatus = NFCSTATUS_SUCCESS;
    uint16_t wFrameLen = 0;
    uint16_t wCrcVal;
    uint8_t *pFrameByte;

    if(NULL == pDlContext)
    {
        NXPLOG_FWDNLD_E("Invalid Input Parameter!!");
        wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_INVALID_PARAMETER);
    }
    else
    {
        if(phDnldNfc_FTWrite == (pDlContext->FrameInp.Type))
        {
            if((0 == (pDlContext->tUserData.wLen)) || (NULL == (pDlContext->tUserData.pBuff)))
            {
                NXPLOG_FWDNLD_E("Invalid Input Parameter(s) for Write!!");
                wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_INVALID_PARAMETER);
            }
            else
            {
                if(TRUE == (pDlContext->tRWInfo.bFirstWrReq))
                {
                    (pDlContext->tRWInfo.wRemBytes) = (pDlContext->tUserData.wLen);
                    (pDlContext->tRWInfo.wOffset) = 0;
                }
            }
        }
        else if(phDnldNfc_FTRead == (pDlContext->FrameInp.Type))
        {
            if((0 == (pDlContext->tRspBuffInfo.wLen)) || (NULL == (pDlContext->tRspBuffInfo.pBuff)))
            {
                NXPLOG_FWDNLD_E("Invalid Input Parameter(s) for Read!!");
                wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_INVALID_PARAMETER);
            }
            else
            {
                if(FALSE == (pDlContext->tRWInfo.bFramesSegmented))
                {
                    NXPLOG_FWDNLD_D("Verifying RspBuffInfo for Read Request..");
                    wFrameLen = (pDlContext->tRspBuffInfo.wLen) + PHDNLDNFC_MIN_PLD_LEN;

                    (pDlContext->tRWInfo.wRWPldSize) = (PHDNLDNFC_CMDRESP_MAX_PLD_SIZE - PHDNLDNFC_MIN_PLD_LEN);
                    (pDlContext->tRWInfo.wRemBytes) = (pDlContext->tRspBuffInfo.wLen);
                    (pDlContext->tRWInfo.dwAddr) = (pDlContext->FrameInp.dwAddr);
                    (pDlContext->tRWInfo.wOffset) = 0;
                    (pDlContext->tRWInfo.wBytesRead) = 0;

                    if(PHDNLDNFC_CMDRESP_MAX_PLD_SIZE < wFrameLen)
                    {
                        (pDlContext->tRWInfo.bFramesSegmented) = TRUE;
                    }
                }
            }
        }
        else if(phDnldNfc_FTLog == (pDlContext->FrameInp.Type))
        {
            if((0 == (pDlContext->tUserData.wLen)) || (NULL == (pDlContext->tUserData.pBuff)))
            {
                NXPLOG_FWDNLD_E("Invalid Input Parameter(s) for Log!!");
                wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_INVALID_PARAMETER);
            }
        }
        else
        {
        }

        if(NFCSTATUS_SUCCESS == wStatus)
        {
            wStatus = phDnldNfc_CreateFramePld(pDlContext);
        }

        if(NFCSTATUS_SUCCESS == wStatus)
        {
            wFrameLen = 0;
            wFrameLen  = (pDlContext->tCmdRspFrameInfo.dwSendlength);

            if(phDnldNfc_FTRaw != (pDlContext->FrameInp.Type))
            {
                if(phDnldNfc_FTWrite != (pDlContext->FrameInp.Type))
                {
                    pFrameByte = (uint8_t *)&wFrameLen;

                    pDlContext->tCmdRspFrameInfo.aFrameBuff[PHDNLDNFC_FRAME_HDR_OFFSET] = pFrameByte[1];
                    pDlContext->tCmdRspFrameInfo.aFrameBuff[PHDNLDNFC_FRAME_HDR_OFFSET + 1] = pFrameByte[0];

                    NXPLOG_FWDNLD_D("Inserting FrameId ..");
                    pDlContext->tCmdRspFrameInfo.aFrameBuff[PHDNLDNFC_FRAMEID_OFFSET] =
                        (pDlContext->tCmdId);

                    wFrameLen += PHDNLDNFC_FRAME_HDR_LEN;
                }
                else
                {
                    if(0 != (pDlContext->tRWInfo.wRWPldSize))
                    {
                        if(TRUE == (pDlContext->tRWInfo.bFramesSegmented))
                        {
                            /* Turning ON the Fragmentation bit in FrameLen */
                            wFrameLen = PHDNLDNFC_SET_HDR_FRAGBIT(wFrameLen);
                        }

                        pFrameByte = (uint8_t *)&wFrameLen;

                        pDlContext->tCmdRspFrameInfo.aFrameBuff[PHDNLDNFC_FRAME_HDR_OFFSET] = pFrameByte[1];
                        pDlContext->tCmdRspFrameInfo.aFrameBuff[PHDNLDNFC_FRAME_HDR_OFFSET + 1] = pFrameByte[0];

                        /* To ensure we have no frag bit set for crc calculation */
                        wFrameLen = PHDNLDNFC_CLR_HDR_FRAGBIT(wFrameLen);

                        wFrameLen += PHDNLDNFC_FRAME_HDR_LEN;
                    }
                }
                if(wFrameLen > PHDNLDNFC_CMDRESP_MAX_BUFF_SIZE - 2)
                {
                    NXPLOG_FWDNLD_D("wFrameLen exceeds the limit");
                    return NFCSTATUS_FAILED;
                }
                /* calculate CRC16 */
                wCrcVal = phDnldNfc_CalcCrc16((pDlContext->tCmdRspFrameInfo.aFrameBuff),wFrameLen);

                pFrameByte = (uint8_t *)&wCrcVal;

                /* Insert the computed Crc value */
                pDlContext->tCmdRspFrameInfo.aFrameBuff[wFrameLen] = pFrameByte[1];
                pDlContext->tCmdRspFrameInfo.aFrameBuff[wFrameLen+ 1] = pFrameByte[0];

                wFrameLen += PHDNLDNFC_FRAME_CRC_LEN;
            }

            (pDlContext->tCmdRspFrameInfo.dwSendlength) = wFrameLen;
            NXPLOG_FWDNLD_D("Frame created successfully");
        }
        else
        {
            NXPLOG_FWDNLD_E("Frame creation failed!!");
            wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_FAILED);
        }
    }

    return wStatus;
}

/*******************************************************************************
**
** Function         phDnldNfc_CreateFramePld
**
** Description      Forms the frame payload
**
** Parameters       pDlContext - pointer to the download context structure
**
** Returns          NFC status
**
*******************************************************************************/
static NFCSTATUS phDnldNfc_CreateFramePld(pphDnldNfc_DlContext_t pDlContext)
{
    NFCSTATUS wStatus = NFCSTATUS_SUCCESS;
    uint16_t wBuffIdx = 0;
    uint16_t wChkIntgVal = 0;
    uint16_t wFrameLen = 0;

    if(NULL == pDlContext)
    {
        NXPLOG_FWDNLD_E("Invalid Input Parameter!!");
        wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_INVALID_PARAMETER);
    }
    else
    {
        memset((pDlContext->tCmdRspFrameInfo.aFrameBuff),0,PHDNLDNFC_CMDRESP_MAX_BUFF_SIZE);
        (pDlContext->tCmdRspFrameInfo.dwSendlength) = 0;

        if(phDnldNfc_FTNone == (pDlContext->FrameInp.Type))
        {
            (pDlContext->tCmdRspFrameInfo.dwSendlength) += PHDNLDNFC_MIN_PLD_LEN;
        }
        else if(phDnldNfc_ChkIntg == (pDlContext->FrameInp.Type))
        {
            (pDlContext->tCmdRspFrameInfo.dwSendlength) += PHDNLDNFC_MIN_PLD_LEN;

            wChkIntgVal = PHDNLDNFC_USERDATA_EEPROM_OFFSET;
            memcpy(&(pDlContext->tCmdRspFrameInfo.aFrameBuff[PHDNLDNFC_FRAME_RDDATA_OFFSET]),
                        &wChkIntgVal,sizeof(wChkIntgVal));

            wChkIntgVal = PHDNLDNFC_USERDATA_EEPROM_LEN;
            memcpy(&(pDlContext->tCmdRspFrameInfo.aFrameBuff[PHDNLDNFC_FRAME_RDDATA_OFFSET +
                PHDNLDNFC_USERDATA_EEPROM_OFFSIZE]),&wChkIntgVal,sizeof(wChkIntgVal));

            (pDlContext->tCmdRspFrameInfo.dwSendlength) += PHDNLDNFC_USERDATA_EEPROM_LENSIZE;
            (pDlContext->tCmdRspFrameInfo.dwSendlength) += PHDNLDNFC_USERDATA_EEPROM_OFFSIZE;
        }
        else if(phDnldNfc_FTWrite == (pDlContext->FrameInp.Type))
        {
            wBuffIdx = (pDlContext->tRWInfo.wOffset);

            if(FALSE == (pDlContext->tRWInfo.bFramesSegmented))
            {
                wFrameLen = (pDlContext->tUserData.pBuff[wBuffIdx]);
                wFrameLen <<= 8;
                wFrameLen |= (pDlContext->tUserData.pBuff[wBuffIdx + 1]);

                (pDlContext->tRWInfo.wRWPldSize) = wFrameLen;
            }

            if((pDlContext->tRWInfo.wRWPldSize) > PHDNLDNFC_CMDRESP_MAX_PLD_SIZE)
            {
                if(FALSE == (pDlContext->tRWInfo.bFirstChunkResp))
                {
                    (pDlContext->tRWInfo.wRemChunkBytes) = wFrameLen;
                    (pDlContext->tRWInfo.wOffset) += PHDNLDNFC_FRAME_HDR_LEN;
                    wBuffIdx = (pDlContext->tRWInfo.wOffset);
                }

                if(PHDNLDNFC_CMDRESP_MAX_PLD_SIZE < (pDlContext->tRWInfo.wRemChunkBytes))
                {
                    (pDlContext->tRWInfo.wBytesToSendRecv) = PHDNLDNFC_CMDRESP_MAX_PLD_SIZE;
                    (pDlContext->tRWInfo.bFramesSegmented) = TRUE;
                }
                else
                {
                    (pDlContext->tRWInfo.wBytesToSendRecv) = (pDlContext->tRWInfo.wRemChunkBytes);
                    (pDlContext->tRWInfo.bFramesSegmented) = FALSE;
                }

                memcpy(&(pDlContext->tCmdRspFrameInfo.aFrameBuff[PHDNLDNFC_FRAMEID_OFFSET]),
                        &(pDlContext->tUserData.pBuff[wBuffIdx]),(pDlContext->tRWInfo.wBytesToSendRecv));
            }
            else
            {
                (pDlContext->tRWInfo.wRWPldSize) = 0;
                (pDlContext->tRWInfo.wBytesToSendRecv) = (wFrameLen + PHDNLDNFC_FRAME_HDR_LEN);

                memcpy(&(pDlContext->tCmdRspFrameInfo.aFrameBuff[0]),
                    &(pDlContext->tUserData.pBuff[wBuffIdx]),(pDlContext->tRWInfo.wBytesToSendRecv));
            }
            (pDlContext->tCmdRspFrameInfo.dwSendlength) += (pDlContext->tRWInfo.wBytesToSendRecv);
        }
        else if(phDnldNfc_FTRead == (pDlContext->FrameInp.Type))
        {
            (pDlContext->tRWInfo.wBytesToSendRecv) = ((pDlContext->tRWInfo.wRemBytes) >
                    (pDlContext->tRWInfo.wRWPldSize)) ? (pDlContext->tRWInfo.wRWPldSize) :
                    (pDlContext->tRWInfo.wRemBytes);

            wBuffIdx = (PHDNLDNFC_PLD_OFFSET + ((sizeof(pDlContext->tRWInfo.wBytesToSendRecv))
                        % PHDNLDNFC_MIN_PLD_LEN) - 1);

            memcpy(&(pDlContext->tCmdRspFrameInfo.aFrameBuff[wBuffIdx]),
                &(pDlContext->tRWInfo.wBytesToSendRecv),(sizeof(pDlContext->tRWInfo.wBytesToSendRecv)));

            wBuffIdx += sizeof(pDlContext->tRWInfo.wBytesToSendRecv);

            memcpy(&(pDlContext->tCmdRspFrameInfo.aFrameBuff[wBuffIdx]),
                &(pDlContext->tRWInfo.dwAddr),sizeof(pDlContext->tRWInfo.dwAddr));

            (pDlContext->tCmdRspFrameInfo.dwSendlength) += (PHDNLDNFC_MIN_PLD_LEN +
                (sizeof(pDlContext->tRWInfo.dwAddr)));
        }
        else if(phDnldNfc_FTLog == (pDlContext->FrameInp.Type))
        {
            (pDlContext->tCmdRspFrameInfo.dwSendlength) += PHDNLDNFC_MIN_PLD_LEN;

            wBuffIdx = (PHDNLDNFC_MIN_PLD_LEN + PHDNLDNFC_FRAME_HDR_LEN);

            memcpy(&(pDlContext->tCmdRspFrameInfo.aFrameBuff[wBuffIdx]),
                (pDlContext->tUserData.pBuff),(pDlContext->tUserData.wLen));

            (pDlContext->tCmdRspFrameInfo.dwSendlength) += (pDlContext->tUserData.wLen);
        }
        else if(phDnldNfc_FTForce == (pDlContext->FrameInp.Type))
        {
            (pDlContext->tCmdRspFrameInfo.dwSendlength) += PHDNLDNFC_MIN_PLD_LEN;

            wBuffIdx = PHDNLDNFC_PLD_OFFSET;

            memcpy(&(pDlContext->tCmdRspFrameInfo.aFrameBuff[wBuffIdx]),
                (pDlContext->tUserData.pBuff),(pDlContext->tUserData.wLen));
        }
        else if(phDnldNfc_FTRaw == (pDlContext->FrameInp.Type))
        {
            if((0 == (pDlContext->tUserData.wLen)) || (NULL == (pDlContext->tUserData.pBuff)))
            {
                NXPLOG_FWDNLD_E("Invalid Input Parameter(s) for Raw Request!!");
                wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_INVALID_PARAMETER);
            }
            else
            {
                memcpy(&(pDlContext->tCmdRspFrameInfo.aFrameBuff[wBuffIdx]),
                    (pDlContext->tUserData.pBuff),(pDlContext->tUserData.wLen));

                (pDlContext->tCmdRspFrameInfo.dwSendlength) += (pDlContext->tUserData.wLen);
            }
        }
        else
        {
        }
    }

    return wStatus;
}

/*******************************************************************************
**
** Function         phDnldNfc_ProcessFrame
**
** Description      Processes response frame received
**
** Parameters       pContext - pointer to the download context structure
**                  pInfo    - pointer to the Transaction buffer updated by TML Thread
**
** Returns          NFCSTATUS_SUCCESS               - parameters successfully validated
**                  NFCSTATUS_INVALID_PARAMETER     - invalid parameters
**
*******************************************************************************/
static NFCSTATUS phDnldNfc_ProcessFrame(void *pContext, phTmlNfc_TransactInfo_t *pInfo)
{
    NFCSTATUS wStatus = NFCSTATUS_SUCCESS;
    uint16_t wCrcVal,wRecvdCrc,wRecvdLen,wPldLen;
    pphDnldNfc_DlContext_t pDlCtxt = (pphDnldNfc_DlContext_t)pContext;

    if((NULL == pDlCtxt) ||
       (NULL == pInfo)
       )
    {
        NXPLOG_FWDNLD_E("Invalid Input Parameters!!");
        wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_INVALID_PARAMETER);
    }
    else
    {
        if((PH_DL_STATUS_OK != pInfo->wStatus) ||
            (0 == pInfo->wLength) ||
            (NULL == pInfo->pBuff))
        {
            NXPLOG_FWDNLD_E("Dnld Cmd Request Failed!!");
            wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_FAILED);
        }
        else
        {
            if(phDnldNfc_FTRaw == (pDlCtxt->FrameInp.Type))
            {
              if((0 != (pDlCtxt->tRspBuffInfo.wLen)) &&
                    (NULL != (pDlCtxt->tRspBuffInfo.pBuff)))
              {
                  memcpy((pDlCtxt->tRspBuffInfo.pBuff),(pInfo->pBuff),(pInfo->wLength));

                  (pDlCtxt->tRspBuffInfo.wLen) = (pInfo->wLength);
              }
              else
              {
                  NXPLOG_FWDNLD_E("Cannot update Response buff with received data!!");
              }
            }
            else
            {
                /* calculate CRC16 */
                wCrcVal = phDnldNfc_CalcCrc16((pInfo->pBuff),((pInfo->wLength) - PHDNLDNFC_FRAME_CRC_LEN));

                wRecvdCrc = 0;
                wRecvdCrc = (((uint16_t)(pInfo->pBuff[(pInfo->wLength) - 2]) << 8U) |
                               (pInfo->pBuff[(pInfo->wLength) - 1]));

                if(wRecvdCrc == wCrcVal)
                {
                    wRecvdLen = (((uint16_t)(pInfo->pBuff[PHDNLDNFC_FRAME_HDR_OFFSET]) << 8U) |
                               (pInfo->pBuff[PHDNLDNFC_FRAME_HDR_OFFSET + 1]));

                    wPldLen = ((pInfo->wLength) - (PHDNLDNFC_FRAME_HDR_LEN + PHDNLDNFC_FRAME_CRC_LEN));

                    if(wRecvdLen != wPldLen)
                    {
                        NXPLOG_FWDNLD_E("Invalid frame payload length received");
                        wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_FAILED);
                    }
                    else
                    {
                        wStatus = phDnldNfc_UpdateRsp(pDlCtxt,pInfo,(wPldLen - 1));
                    }
                }
                else
                {
                    NXPLOG_FWDNLD_E("Invalid frame received");
                    wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_FAILED);
                }
            }
        }
    }

    return wStatus;
}

/*******************************************************************************
**
** Function         phDnldNfc_ProcessRecvInfo
**
** Description      Processes the response during the state phDnldNfc_StateRecv
**
** Parameters       pContext - pointer to the download context structure
**                  pInfo    - pointer to the Transaction buffer updated by TML Thread
**
** Returns          NFCSTATUS_SUCCESS               - parameters successfully validated
**                  NFCSTATUS_INVALID_PARAMETER     - invalid parameters
**
*******************************************************************************/
static NFCSTATUS phDnldNfc_ProcessRecvInfo(void *pContext, phTmlNfc_TransactInfo_t *pInfo)
{
    NFCSTATUS wStatus = NFCSTATUS_SUCCESS;

    if(NULL != pContext)
    {
        if (NULL == pInfo)
        {
            NXPLOG_FWDNLD_E("Invalid pInfo received from TML!!");
            wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_FAILED);
        }
        else
        {
            wStatus = PHNFCSTATUS(pInfo->wStatus);

            if(NFCSTATUS_SUCCESS == wStatus)
            {
                NXPLOG_FWDNLD_D("Send Success");
            }else
            {
                NXPLOG_FWDNLD_E("Tml Write error!!");
                 wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_FAILED);
            }
        }
    }
    else
    {
        NXPLOG_FWDNLD_E("Invalid context received from TML!!");
         wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_FAILED);
    }

    return wStatus;
}

/*******************************************************************************
**
** Function         phDnldNfc_SetupResendTimer
**
** Description      Sets up the timer for resending the previous write frame
**
** Parameters       pDlContext - pointer to the download context structure
**
** Returns          NFC status
**
*******************************************************************************/
static NFCSTATUS phDnldNfc_SetupResendTimer(pphDnldNfc_DlContext_t pDlContext)
{
    NFCSTATUS wStatus = NFCSTATUS_SUCCESS;

    wStatus = phOsalNfc_Timer_Start((pDlContext->TimerInfo.dwRspTimerId),
                                                PHDNLDNFC_RETRY_FRAME_WRITE,
                                                &phDnldNfc_ResendTimeOutCb,
                                                pDlContext);

    if(NFCSTATUS_SUCCESS == wStatus)
    {
        NXPLOG_FWDNLD_D("Frame Resend wait timer started");
        (pDlContext->TimerInfo.TimerStatus) = 1;
        pDlContext->tCurrState = phDnldNfc_StateTimer;
    }
    else
    {
         NXPLOG_FWDNLD_W("Frame Resend wait timer not started");
         (pDlContext->TimerInfo.TimerStatus) = 0;/*timer stopped*/
         pDlContext->tCurrState = phDnldNfc_StateResponse;
        /* Todo:- diagnostic in this case */
    }

    return wStatus;
}

#if !defined(PH_LIBNFC_VEN_RESET_ON_DOWNLOAD_TIMEOUT)
#   error PH_LIBNFC_VEN_RESET_ON_DOWNLOAD_TIMEOUT has to be defined
#endif

/*******************************************************************************
**
** Function         phDnldNfc_RspTimeOutCb
**
** Description      Callback function in case of timer expiration
**
** Parameters       TimerId  - expired timer id
**                  pContext - pointer to the download context structure
**
** Returns          None
**
*******************************************************************************/
static void phDnldNfc_RspTimeOutCb(uint32_t TimerId, void *pContext)
{
    pphDnldNfc_DlContext_t pDlCtxt = (pphDnldNfc_DlContext_t)pContext;

    if (NULL != pDlCtxt)
    {
        UNUSED(TimerId);

        if(1 == pDlCtxt->TimerInfo.TimerStatus)
        {
            /* No response received and the timer expired */
            pDlCtxt->TimerInfo.TimerStatus = 0;    /* Reset timer status flag */

            NXPLOG_FWDNLD_D("%x",pDlCtxt->tLastStatus);

#if PH_LIBNFC_VEN_RESET_ON_DOWNLOAD_TIMEOUT
            if ( PH_DL_STATUS_SIGNATURE_ERROR  == pDlCtxt->tLastStatus ) {
                /* Do a VEN Reset of the chip. */
                NXPLOG_FWDNLD_E("Performing a VEN Reset");
                phTmlNfc_IoCtl(phTmlNfc_e_EnableNormalMode);
                phTmlNfc_IoCtl(phTmlNfc_e_EnableDownloadMode);
                NXPLOG_FWDNLD_E("VEN Reset Done");
            }
#endif


            (pDlCtxt->TimerInfo.wTimerExpStatus) = NFCSTATUS_RF_TIMEOUT;

            if((phDnldNfc_EventRead == pDlCtxt->tCurrEvent) || (phDnldNfc_EventWrite == pDlCtxt->tCurrEvent))
            {
                phDnldNfc_ProcessRWSeqState(pDlCtxt,NULL);
            }
            else
            {
                phDnldNfc_ProcessSeqState(pDlCtxt,NULL);
            }
        }
    }

    return;
}

/*******************************************************************************
**
** Function         phDnldNfc_ResendTimeOutCb
**
** Description      Callback function in case of Frame Resend Wait timer expiration
**
** Parameters       TimerId  - expired timer id
**                  pContext - pointer to the download context structure
**
** Returns          None
**
*******************************************************************************/
static void phDnldNfc_ResendTimeOutCb(uint32_t TimerId, void *pContext)
{
    pphDnldNfc_DlContext_t pDlCtxt = (pphDnldNfc_DlContext_t)pContext;

    if (NULL != pDlCtxt)
    {
        UNUSED(TimerId);

        if(1 == pDlCtxt->TimerInfo.TimerStatus)
        {
            /* No response received and the timer expired */
            pDlCtxt->TimerInfo.TimerStatus = 0;    /* Reset timer status flag */

            (pDlCtxt->TimerInfo.wTimerExpStatus) = 0;

             pDlCtxt->tCurrState = phDnldNfc_StateSend;

             /* set the flag to trigger last frame re-transmission */
             pDlCtxt->bResendLastFrame = TRUE;

             phDnldNfc_ProcessRWSeqState(pDlCtxt,NULL);
        }
    }

    return;
}

/*******************************************************************************
**
** Function         phDnldNfc_UpdateRsp
**
** Description      verifies the payload status byte and copies data
**                  to response buffer if successful
**
** Parameters       pDlContext - pointer to the download context structure
**                  pInfo      - pointer to the Transaction buffer updated by TML Thread
**                  wPldLen    - Length of the payload bytes to copy to response buffer
**
** Returns          NFC status
**
*******************************************************************************/
static NFCSTATUS phDnldNfc_UpdateRsp(pphDnldNfc_DlContext_t   pDlContext, phTmlNfc_TransactInfo_t  *pInfo, uint16_t wPldLen)
{
    NFCSTATUS wStatus = NFCSTATUS_SUCCESS;
    uint16_t wReadLen = 0;

    if((NULL == pDlContext) || (NULL == pInfo))
    {
        NXPLOG_FWDNLD_E("Invalid Input Parameters!!");
        wStatus = PHNFCSTVAL(CID_NFC_DNLD,NFCSTATUS_INVALID_PARAMETER);
    }
    else
    {
        if(PH_DL_CMD_WRITE == (pDlContext->tCmdId))
        {
            if(PH_DL_STATUS_OK == (pInfo->pBuff[PHDNLDNFC_FRAMESTATUS_OFFSET]))
            {
                /* first write frame response received case */
                if(TRUE == (pDlContext->tRWInfo.bFirstWrReq))
                {
                    NXPLOG_FWDNLD_D("First Write Frame Success Status received!!");
                    (pDlContext->tRWInfo.bFirstWrReq) = FALSE;
                }

                if(TRUE == (pDlContext->tRWInfo.bFirstChunkResp))
                {
                    if(FALSE == (pDlContext->tRWInfo.bFramesSegmented))
                    {
                        NXPLOG_FWDNLD_D("Chunked Write Frame Success Status received!!");
                        (pDlContext->tRWInfo.wRemChunkBytes) -= (pDlContext->tRWInfo.wBytesToSendRecv);
                        (pDlContext->tRWInfo.bFirstChunkResp) = FALSE;
                    }
                    else
                    {
                        NXPLOG_FWDNLD_E("UnExpected Status received!!");
                        wStatus = PHNFCSTVAL(CID_NFC_DNLD, NFCSTATUS_FAILED);
                    }
                }

                if(NFCSTATUS_SUCCESS == wStatus)
                {
                    (pDlContext->tRWInfo.wRemBytes) -= (pDlContext->tRWInfo.wBytesToSendRecv);
                    (pDlContext->tRWInfo.wOffset) += (pDlContext->tRWInfo.wBytesToSendRecv);
                }
            }
            else if((FALSE == (pDlContext->tRWInfo.bFirstChunkResp)) &&
                (TRUE == (pDlContext->tRWInfo.bFramesSegmented)) &&
                (PHDNLDNFC_FIRST_FRAGFRAME_RESP == (pInfo->pBuff[PHDNLDNFC_FRAMESTATUS_OFFSET])))
            {
                (pDlContext->tRWInfo.bFirstChunkResp) = TRUE;
                (pDlContext->tRWInfo.wRemChunkBytes) -= (pDlContext->tRWInfo.wBytesToSendRecv);
                (pDlContext->tRWInfo.wRemBytes) -= ((pDlContext->tRWInfo.wBytesToSendRecv) + PHDNLDNFC_FRAME_HDR_LEN);
                (pDlContext->tRWInfo.wOffset) += (pDlContext->tRWInfo.wBytesToSendRecv);

                /* first write frame response received case */
                if(TRUE == (pDlContext->tRWInfo.bFirstWrReq))
                {
                    NXPLOG_FWDNLD_D("First Write Frame Success Status received!!");
                    (pDlContext->tRWInfo.bFirstWrReq) = FALSE;
                }
            }
            else if((TRUE == (pDlContext->tRWInfo.bFirstChunkResp)) &&
                (TRUE == (pDlContext->tRWInfo.bFramesSegmented)) &&
                (PHDNLDNFC_NEXT_FRAGFRAME_RESP == (pInfo->pBuff[PHDNLDNFC_FRAMESTATUS_OFFSET])))
            {
                (pDlContext->tRWInfo.wRemChunkBytes) -= (pDlContext->tRWInfo.wBytesToSendRecv);
                (pDlContext->tRWInfo.wRemBytes) -= (pDlContext->tRWInfo.wBytesToSendRecv);
                (pDlContext->tRWInfo.wOffset) += (pDlContext->tRWInfo.wBytesToSendRecv);
            }
            else if(PH_DL_STATUS_FIRMWARE_VERSION_ERROR == (pInfo->pBuff[PHDNLDNFC_FRAMESTATUS_OFFSET]))
            {
                NXPLOG_FWDNLD_E("FW version Error !!!could be either due to FW major version mismatch or Firmware Already Up To Date !!");
                (pDlContext->tRWInfo.bFirstWrReq) = FALSE;
                /* resetting wRemBytes to 0 to avoid any further write frames send */
                (pDlContext->tRWInfo.wRemBytes) = 0;
                (pDlContext->tRWInfo.wOffset) = 0;
                wStatus = NFCSTATUS_FW_VERSION_ERROR;
            }
            else if(PH_DL_STATUS_PLL_ERROR == (pInfo->pBuff[PHDNLDNFC_FRAMESTATUS_OFFSET]))
            {
                NXPLOG_FWDNLD_E("PLL Error Status received!!");
                (pDlContext->tLastStatus) = PH_DL_STATUS_PLL_ERROR;
                wStatus = NFCSTATUS_WRITE_FAILED;
            }
            else if(PH_DL_STATUS_SIGNATURE_ERROR == (pInfo->pBuff[PHDNLDNFC_FRAMESTATUS_OFFSET]))
            {
                NXPLOG_FWDNLD_E("Signature Mismatch Error received!!");
                /* save the status for use in loading the relevant recovery image (either signature or platform) */
                (pDlContext->tLastStatus) = PH_DL_STATUS_SIGNATURE_ERROR;
                wStatus = NFCSTATUS_REJECTED;
            }
            else if(PH_DL_STATUS_MEM_BSY == (pInfo->pBuff[PHDNLDNFC_FRAMESTATUS_OFFSET]))
            {
                NXPLOG_FWDNLD_E("Mem Busy Status received!!");
                (pDlContext->tLastStatus) = PH_DL_STATUS_MEM_BSY;
                wStatus = NFCSTATUS_BUSY;
            }
            else
            {
                NXPLOG_FWDNLD_E("Unsuccessful Status received!!");
                wStatus = PHNFCSTVAL(CID_NFC_DNLD, NFCSTATUS_FAILED);
            }
        }
        else if(PH_DL_CMD_READ == (pDlContext->tCmdId))
        {
            if(PH_DL_STATUS_OK == (pInfo->pBuff[PHDNLDNFC_FRAMESTATUS_OFFSET]))
            {
                wReadLen = (((uint16_t)(pInfo->pBuff[PHDNLDNFC_FRAMESTATUS_OFFSET + 3]) << 8U) |
                           (pInfo->pBuff[PHDNLDNFC_FRAMESTATUS_OFFSET + 2]));

                if(wReadLen != (pDlContext->tRWInfo.wBytesToSendRecv))
                {
                    NXPLOG_FWDNLD_E("Desired Length bytes not received!!");
                    wStatus = PHNFCSTVAL(CID_NFC_DNLD, NFCSTATUS_FAILED);
                }
                else
                {
                    memcpy(&(pDlContext->tRspBuffInfo.pBuff[(pDlContext->tRWInfo.wOffset)]),
                        &(pInfo->pBuff[PHDNLDNFC_FRAME_RDDATA_OFFSET]),
                        wReadLen);

                    (pDlContext->tRWInfo.wBytesRead) += wReadLen;

                    (pDlContext->tRspBuffInfo.wLen) = (pDlContext->tRWInfo.wBytesRead);

                    (pDlContext->tRWInfo.wRemBytes) -= (pDlContext->tRWInfo.wBytesToSendRecv);
                    (pDlContext->tRWInfo.dwAddr) += (pDlContext->tRWInfo.wBytesToSendRecv);
                    (pDlContext->tRWInfo.wOffset) += (pDlContext->tRWInfo.wBytesToSendRecv);
                }
            }
            else
            {
                NXPLOG_FWDNLD_E("Unsuccessful Status received!!");
                wStatus = PHNFCSTVAL(CID_NFC_DNLD, NFCSTATUS_FAILED);
            }
        }
        else
        {
            if(PH_DL_STATUS_OK == (pInfo->pBuff[PHDNLDNFC_FRAMESTATUS_OFFSET]))
            {
                if((0 != (pDlContext->tRspBuffInfo.wLen)) &&
                    (NULL != (pDlContext->tRspBuffInfo.pBuff)))
                {
                    memcpy((pDlContext->tRspBuffInfo.pBuff),&(pInfo->pBuff[PHDNLDNFC_FRAMESTATUS_OFFSET + 1]),
                        wPldLen);

                    (pDlContext->tRspBuffInfo.wLen) = wPldLen;
                }
            }
            else
            {
                NXPLOG_FWDNLD_E("Unsuccessful Status received!!");
                wStatus = PHNFCSTVAL(CID_NFC_DNLD, NFCSTATUS_FAILED);
            }
        }
    }

    return wStatus;
}
